04 Django 模型基础
一、models 字段类型
django 根据属性的类型确定以下信息:
当前选择的数据库支持字段的类型
渲染管理表单时使用的默认 html 控件
在管理站点最低限度的验证
注意:django 会自动为表添加自动增长的主键列,每个模型只能有一个主键列。如果使用选项设置某属性为主键列后,django 则不会再生成默认的主键列。
属性命名限制
- 遵循标识符规则
- 由于 django 的查询方式,不允许使用连续的下划线(两个下划线
__)
定义属性
定义属性时,需要的字段类型被定义在 django.db.models.fields 目录下。为了方便使用,通常会导入到 django.db.models 包,使用 models.Field类型名 定义字段。
from django.db import models
class UserModels(models.Model):
name = models.CharField(max_length=30)
# 省略后续代码
逻辑删除和物理删除
对于重要的数据都做逻辑删除,不做物理删除。实现方法是定义 is_delete 属性,类型为 BooleanField,默认值为 False。
is_delete = models.BooleanField(default=False)
常用字段类型
AutoField:根据实际ID自动增长的IntegerField。通常不指定,如果指定,主键字段id将自动添加到模型中。CharField(max_length=字符长度:字符串,默认的表单样式是Input。TextField:大文本字段。一般超过4000使用,默认的表单控件是Textarea。IntegerField:整数。DecimalField(max_digits=None, decimal_places=None):- 使用
python的Decimal实例表示的十进制浮点数 - 参数说明
max_digits:位数总数decimal_places:小数点后的位数
- 使用
FloatField:用Python的float实例来表示的浮点数BooleanField:True / False,默认表单控件是CheckboxInputDateField([auto_now=False, auto_now_add=Flase]):- 使用
Python的datetime.date实例表示的日期 - 参数:
auto_now:每次保存对象时,自动设置该字段为当前时间,用于==最后一次修改的时间戳==,它总使用当前日期,默认为falseauto_now_add:当对像第一次被创建时自动设置当前时间,用于==创建的时间戳==,它总是使用创建的日期,默认为false
- 注意:
auto_now,auto_add_now,default相互排斥
- 使用
TimeField:使用Python的datetime.time实例表示的时间,参数同DateField- DateTimeField:使用
Python的datetime.datetime实例表示的日期和时间中,参数同DateField FileField:上传文件的字段ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的Image。需要安装Pillow包(pip install pillow)
二、常用字段参数
常用字段参数(选项),可以实现对字段的约束
null=True:数据库中字段是否可以为空
blank=True:django 的 Admin 中添加数据时是否可允许空值
一般 null=True & blank=True 搭配使用,出现 null=True 就会用上 blank=True
primary_key=True:主键,对 AutoField 设置主键后,就会代替原来的自增 id 列
auto_now :自动创建。无论添加或修改,都是当前操作的时间
auto_add_now:自动创建。永远都是创建时的时间
choices:后参 admin 下拉菜单
USER_TYPE_LIST = (
(1, "超级用户"),
(2, "普通用户"),
)
user_type = models.IntegerField(choices=USER_TPYE_LIST, default=1, verbose_name="用户类型")
max_length:最大长度
default:默认值
verbose_name:admin 中字段的显示名称,后台显示的名称
name|db_column :数据库中的字段名称
unique=True:不允许重复
db_index=True:数据库索引。例如:如果你想通过 name 查询的更快的话,给他设置为索引即可
editable=True:在 Admin 里是否可以编辑,不可编辑则不显示
设置表名:
class Meta:
db_tale = "person"
三、models 基本操作
一般数据库操作流程:
- 创建数据库,设计表结构和字段
- 连接
mysql数据库,并编写数据访问层代码 - 业务逻辑层去调用数据访问层执行数据库操作
Django 通过 Model 操作数据库,不管数据的类型是 mysql 还是 sqlite,Django 自动生成相应数据库类型的 SQL 语句,所以不需要关注 SQL 语句和类型,对数据的操作 Django 自动完成,只要写 Model 就可以了。
Django 使用对象关系映射(Object Relational Mapping, ORM)框架去操控数据库。
ORM 对象关系映射是一种程序技术,用于实现面向对象编程语言中不同类型系统的数据之间的转换。
增删改查
ORM
| 模型 | 对应 | 表 |
|---|---|---|
| 类结构 | - | 表结结构 |
| 对象 | - | 表的一条数据 |
| 类属性 | - | 表的字段 |
models 基本操作
基本操作之增
# 1. 创建对象实例,然后调用 save 方法
obj = Author()
obj.first_name = "zhang"
obj.last_name = "san"
obj.save()
# 2. 创建对象并初始化,再调用 save 方法
obj = Author(first_name="zhang", last_name="san")
obj.save()
# 3. 使用 create 方法
Author.objects.create(first_name="zhang", last_name="san")
# 4. 使用 get_or_create 方法,可以防止重复
Author.objects.get_or_create(first_name="zhang", last_name="san")
基本操作之删
# 使用 Queryset 的 delete 方法
# 删除指定条件的数据
Author.objects.filter(first_name="zhang").delete()
# 删除所有数据
Author.objects.all().delete()
# 注意:objects 不能直接调用 delete 方法
# 使用模型对象的 delete 方法
obj = Author.objects.get(id=5)
obj.delete()
基本操作之改
Author.objects.filter(last_name="dfdf").update(last_name="san")
# 模型没有定义 update 方法,直接给字段赋值并调用 save,能实现 update 的功能,如:
obj = Author.objects.get(id=3)
obj.first_name = "zhang"
obj.save()
# save 更新时会更新所有字段,如果只想更新某个字段,减少数据库操作,提高效率,
# 可以使用 save 函数的 update_fields 参数指定更新的字段
obj.first_name = "li"
obj.save(update_fields=["first_name"]) # update_fields 叁数指定更新字段
基本操作之查
# 基础操作
# get() 获取单条数据,只能是一个
Author.objects.get(id=123)
# get() 如果没有找到符合条件的对象,会引发模型类 .DoesNotExist 异常,如果找到多个,会引发模型
# .MultipleObjectsReturned 异常
# first() 返回查询集 Queryset 中的第一个对象
# last() 返回查询集中的最后一个对象
# count() 返回当前查询集中的对象个数
# exits() 判断查询集中是否有数据,如果有数据返回 True 没有 False
# all() 获取全部数据
Author.objects.all()
# values() 获取指定列的值,可以传多个参数。返回包含字典的列表(保存了字段名和对应的值)
Author.objects.all().values("password")
# values_list() 获取指定列的值,可以传多个参数。返回包含元组的列表(只保存值)
Author.objects.all().values_list("password")
# 进阶操作
# 获取个数
Author.objects.filter(name="seven").count()
# __gt 大于
Author.objects.filter(id__gt=1) # 获取 id 大于 1 的值
# select * from Author where id > 1
# __gte 大于等于
Author.objects.filter(id__gte=1) # 获取 id 大于等于 1 的值
# select * from Author where id >= 1
# __lt 小于
Author.objects.filter(id__lt=10) # 获取 id 小于 10 的值
# select * fro Author where id < 10
# __lte 小于等于
Author.objects.filter(id__lte=10) # 获取 id 小于等于 10 的值
# select * from Author where id <= 10
# 连续区间,大于小的,小于大的
Author.objects.filter(id__qt=1, id__lt=10) # 获取 id 大于 1 且小于 10 的值
# select * from Author where id > 1 and id < 10
# __in 在某个集合中
Author.objects.filter(id__in=[11, 22, 33]) # 获取 id 在 11, 22, 33 中的数据
# select * from Author wehre id in (11, 22, 33)
# exclude 与 in 搭配使用,不在某个集合中,想当于 not in
# 注意:in 是 filter 的一个参数,而 exclude 是 objects 的方法
Author.objects.exclude(id__in=[11, 22, 33])
# select * from Author where id not in (11, 22, 33)
# __contains 包含子字符串
Author.objects.filter(name__contains="ven") # 与 sql 中的 like 相同
# select * from Author where name like "%ven%"
# __icontains 忽略大小写
Author.objects.filter(name__icontains="ven")
Author.objects.filter(name__regex="^ven") # 正则匹配
Author.objects.filter(name__iregex="^ven") # 正则匹配,忽略大小写
# __range 区间范围 between and
Author.objects.filter(age__range=[10, 20])
# __startwidth, __istartwidth 以什么开始,i 忽略大小写
# __endwithd, __iendwidth 以什么结尾,i 忽略大小写
# 上面不带 i 的也忽略大小写
# 排序
# 按 id 升序排
Author.objects.filter(name="seven").order_by("id")
# 按 id 降序排
Author.objects.filter(name="seven").order_by("-id")
# 切片,分页时用
Author.objects.all()[10: 20]
# 查所有数据
Author.objects.all() # 可以
Author.objects.filter() # filter 没有查询条件,也可以查所有
聚合操作
聚合函数:Max,Min,Sum
需要先导入聚合函数才能使用
from django.db import Max, Min, Sum # 导入
# ...
result = PersonModel.objects.aggregate(Max('age')) # 最大值, {"age__max": 666}
result = PersonModel.objects.aggregate(Min('age')) # 最小值, {"age__min": 100}
result = PersonModel.objects.aggregate(Avg('age')) # 平均值, {"age__avg": 194.34}
result = PersonModel.objects.aggregate(Count('age')) # 计数, {"age__count": 6}
result = PersonModel.objects.aggregate(Sum('age')) # 求和, {"age__sum": 1166}
排序
# 升序, 按 age 列升序排列
persons = PersonModel.objects.all().order_by("age")
# 升序,先按 age 列升序排列,如果 age 列有相等的记录,则这些记录再按 id 列升序徘列
persons = PersonModel.objects.all().order_by("age", "id")
# 降序排列,按 id 列降序排列
persons = PersonModel.objects.all().order_by("-id")
分页之手动分页
分页功能,也就是第几页显示第几页的数据,如第 3 页显示第 3 页的数据(这句好像是费话)。
为什么当点击前端的第几页时会显示第几页的数据?后台传给前端的有两个数据,一个是第几页(即页数),另一个是第几页对应的数据。
需要以下几个变量
page:第几页per_page:每页10条记录
第几页,用 for page in range(1, 总页数) 循环即可。但是,模板中不能用 range(1, 总页数)。所以需要先在后台 view 中处理,再传到前端。
# 总页数 = 数据总数 / 每页显示的数据个数 并向上取整
# python 向上取整的函数在 math 包中 math.ceil()
total_data = PersonModel.objects.count()
total_page = math.ceil(total_data / per_page)
page_range = range(1, total_page+1)
将 page_range 传到前端,在前端用 for page in page_range 循环,page 就是第几页
上面说过了,需要两个参数,第一个参数有了(第几页,这里就是 page,第二个参数(第 page 页显示的数据)怎么得?
| 第几页 | 数据范围 | 数据下标范围 | 切片 |
|---|---|---|---|
page=1 |
1 ~ 10 | 0 ~ 9 |
[0: 10] |
page=2 |
11 ~ 20 | 10 ~ 19 |
[10: 20] |
page=3 |
21 ~ 30 | 20 ~ 29 |
[20: 30] |
page=4 |
31 ~ 40 | 30 ~ 39 |
[30: 40] |
| ... | ... | ... | ... |
page=n |
... | ... | [(n-1)*10: n*10] |
page=page |
... | ... | [(page-1)*per_page: page*per_page] |
对所有数据(查询集)进行切片可得当前页(第几页)的数据。
def paginate(request, page=1):
per_page = 10
persons = PersonModels.objects.all()
persons = person[(page-1)*per_page: page*per_page] # 第 page 页显示的数据
persons 就是第 page 页显示的内容。将 page 和 persons 传到前端,就可以准备处理分页了。
data = {
"page_range": page_range,
"persons": persons,
}
return render(request, "paginate.html", data)
还需要写路由
path("paginate/<int:page>/", paginate, name="paginate")
有 page 变量传递,需要写带参数的路由,即 <int:page>。对应的 paginate 视图函数写的什么参数,路由的参数就要写什么。如,def paginate(request, page=1) 用的是 page,路由的参数也得写 page,即 <int:page>
因为前端点击页码跳转后显示对应的数据,有反向解析,所以需要在 path 中指定 name 参数
前端
<ul class="btns">
{% for page in page_range %}
<li>
{# 反向解析 #}
<a href="{% url 'paginate' page %}">
<button>{{ page }}</button>
</a>
</li>
{% endfor %}
</ul>
<hr>
<ul>
{% for person in persons %}
<li>{{ person.name }} - {{ person.age }}</li>
{% endfor %}
</ul>
分页之自动分页
自动分页就是使用 django.core.paginator 包中的 Paginator 对象,自动得到 第几页 和 第几页显示的数据 两个变量。
from django.core.paginator import Paginator
def paginate2(request, page=1):
per_page = 10
all_data = PersonModel.object.all()
paginator = Paginator(all_data, per_page)
persons = paginator.page(page)
pages = paginator.page_range
data = {
"persons": persons,
"pages": pages,
}
return render(request, "paginate2.html", data)